﻿using Jint.Parser;
using RAP.Framework.Libary.DataSource.Model.DataSource;
using RAP.Framework.Libary.DomTemplateBuilder.Element.Parameter;
using RAP.Framework.Libary.SqlTaskParser.Parsers;
using RAP.Framework.Libary.Utils;
using RAP.Framework.Libary.Utils.Factory;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace RAP.Framework.Libary.SqlTaskParser
{
  /// <summary>
  /// SQL指令解析工厂对象
  /// </summary>
  public class SqlPaserFactory : FactoryBase<ISqlTokenParser>, ISqlPaserFactory
  {
    private static int _taskCounter = 0;
    private string _sqlTokenParseRegexTpl = @"\s*({0})[^;]*;";

    /// <summary>
    /// 默认的SQLToken解析正则表达式
    /// </summary>
    public virtual Regex SqlTokenParseRegex { get; set; }

    /// <summary>
    /// 默认的SQL查询结果别名解析正则表达式
    /// </summary>
    public virtual Regex SQLAliasParseRegex { get; set; } = new Regex(@"^\s*[\w\[\]\(\), \*]+:{1}(?!:)", RegexOptions.Multiline | RegexOptions.Compiled);

    /// <summary>
    /// 输出SQL调试语句的正则表达式，用来将SQL变量替换成值
    /// </summary>
    public virtual Regex SQLDebugRegex { get; set; } = new Regex(@"@([^@;',\)=\s]+)", RegexOptions.Multiline | RegexOptions.Compiled);

    private bool _autoGeneratorRegex = true;
    /// <summary>
    /// 是否自动生成正则表达式
    /// </summary>
    protected bool AutoGeneratorRegex
    {
      get
      {
        return _autoGeneratorRegex;
      }
      set
      {
        if (value && !_autoGeneratorRegex)
        {
          _autoGeneratorRegex = value;
          //如果状态从false转变成true，则执行一次正则表达式自动生成过程
          GeneratorSqlTokenParseRegex();
        }
        else
        {
          _autoGeneratorRegex = value;
        }
      }
    }


    public SqlPaserFactory()
    {
      AutoGeneratorRegex = false;
      //注册默认的解析器
      Register("@using", new UsingTokenParser());
      Register("@await", new AwaitTokenParser());
      Register("@results", new ResultsTokenParser());
      Register("@join", new JoinTokenParser());
      Register("@mapping", new MappingTokenParser());
      Register("@param", new ResultParamTokenParser());
      Register("@limit", new ResultLimitTokenParser());
      Register("@trans", new TansactionTokenParser());
      Register("@declare", new DeclareTokenParser());
      AutoGeneratorRegex = true;
    }

    private HashSet<string> _parserKeys = null;
    protected virtual HashSet<string> ParserKeys
    {
      get
      {
        if (_parserKeys == null)
        {
          _parserKeys = Keys.ToHashSet();
        }
        return _parserKeys;
      }
    }

    /// <summary>
    /// 生成解析SQLToken的正则表达式
    /// </summary>
    protected virtual void GeneratorSqlTokenParseRegex()
    {
      if (!AutoGeneratorRegex)
      {
        return;
      }
      var tokenRegexes = new List<string>();
      var customerRegexes = new List<string>();
      foreach (var key in Keys)
      {
        var parser = this[key];
        if (parser.CustomerRegex.IsNotNullOrEmpty())
        {
          customerRegexes.Add(parser.CustomerRegex);
        }
        else
        {
          tokenRegexes.Add($"{key}");
        }
      }
      customerRegexes.Insert(0, _sqlTokenParseRegexTpl.format(tokenRegexes.Join("|")));
      SqlTokenParseRegex = new Regex(customerRegexes.Join("|"));
    }

    /// <summary>
    /// 将SQL语句解析成可执行的多个SqlTask任务
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="defaultDataSourceId"></param>
    /// <param name="tokenRegex"></param>
    /// <param name="processingSqlToken"></param>
    /// <param name="processedSqlToken"></param>
    /// <returns></returns>
    public virtual SqlParseResult ParseSqlTasks(
      string sql,
      DataSourceConfig dataSourceConfig,
      string defaultDataSourceId = null,
      Regex tokenRegex = null,
      Func<SqlParseContext, bool> processingSqlToken = null,
      Func<SqlParseContext, bool> processedSqlToken = null)
    {
      if (sql.IsNullOrEmpty())
      {
        return null;
      }
      if (tokenRegex == null)
      {
        tokenRegex = SqlTokenParseRegex;
      }
      var matchCounter = 0;
      var matches = new List<SqlTokenMatch>();
      sql = tokenRegex.Replace(sql, match =>
      {
        var placeholderTag = $"<SqlToken:{matchCounter++}>";
        matches.Add(new SqlTokenMatch
        {
          Value = match.Value,
          PlaceholderTag = placeholderTag,
        });
        return placeholderTag;
      });
      matches.ForEach(match =>
      {
        match.Index = sql.IndexOf(match.PlaceholderTag);
      });
      var sqlParseContext = new SqlParseContext
      {
        CurrentParallelTasks = new SqlTaskCollection(),
        DefaultDataSourceId = defaultDataSourceId,
        Sql = sql,
        DataSourceConfig = dataSourceConfig,
      };
      sqlParseContext.CreateSqlTask = () =>
      {
        return CreateSqlTask(string.Empty, sqlParseContext);
      };
      var sqlParseResult = sqlParseContext.ParseResult;//SQL编译结果
      var queryTasksQueue = sqlParseResult.SqlTasks;//串行执行的SQL任务

      if (matches.Count > 0)
      {
        #region 处理Token
        //取得第一个Token前面的第一段Sql
        var sqlFirst = sql.Substring(0, matches[0].Index);
        if (sqlFirst.IsNotNullOrEmpty())
        {
          CreateSqlTask(sqlFirst, sqlParseContext);
        }
        for (int i = 0; i < matches.Count; i++)
        {
          var currentTokenMatch = matches[i];
          var tokenExp = currentTokenMatch.Value;
          var sqlToken = GetSqlToken(tokenExp, sqlParseContext, i);
          var startIndex = currentTokenMatch.Index;
          var sqlCurrent = string.Empty;
          if (sqlToken == null)
          {
            //如果未匹配到Token的解析对象，则代表是不自定义Token，而可能是SQL语句中的变量定义，则将当前字符串直接作为SQL输出
            sqlParseContext.CurrentTask.Sql.Append(tokenExp);
            if (i < matches.Count - 1)
            {
              //中间部分的Sql
              var nextTokenMatch = matches[i + 1];
              var sqlLength = nextTokenMatch.Index - startIndex;
              sqlCurrent = sql.Substring(startIndex, sqlLength);
            }
            else
            {
              //处理最后一个Token以及后面的Sql
              sqlCurrent = sql.Substring(startIndex);
            }
            if (sqlCurrent.Trim().Length > 0)
            {
              (var resultAlias, var sqlParsed) = GetSQLAliasList(sqlCurrent.ToString());
              if (sqlParseContext.CurrentTask.ResultAlias == null)
              {
                sqlParseContext.CurrentTask.ResultAlias = resultAlias;
              }
              else
              {
                sqlParseContext.CurrentTask.ResultAlias.Add(resultAlias.ToArray());
              }
              sqlCurrent = sqlParsed;
            }
            sqlParseContext.CurrentTask.Sql.Append(sqlCurrent);
            continue;
          }
          sqlParseResult.Tokens.Add(sqlToken);
          sqlParseContext.CurrentToken = sqlToken;
          if (i < matches.Count - 1)
          {
            //中间部分的Sql
            var nextTokenMatch = matches[i + 1];
            var sqlLength = nextTokenMatch.Index - startIndex;
            sqlCurrent = sql.Substring(startIndex, sqlLength);
          }
          else
          {
            //处理最后一个Token以及后面的Sql
            sqlCurrent = sql.Substring(startIndex);
          }

          if (sqlCurrent.IsNotNullOrEmpty())
          {
            CreateSqlTask(sqlCurrent, sqlParseContext);
          }

          if (processingSqlToken?.Invoke(sqlParseContext) == false)
          {
            //如果回调函数返回false，则终止处理
            return null;
          }

          #region 处理Token
          //****************
          var sqlParser = this[sqlToken.Token];
          if (sqlParser != null)
          {
            sqlParser.ParseToken(sqlParseContext);
            sqlCurrent = sqlParseContext.CurrentTask.Sql.ToString();
          }
          #endregion 处理Token

          if (processedSqlToken?.Invoke(sqlParseContext) == false)
          {
            //如果回调函数返回false，则终止处理
            return null;
          }
          if (sqlCurrent.IsNotNullOrEmpty())
          {
            sqlParseContext.LastTask = CombineSqlTask(sqlCurrent, sqlParseContext);
          }
          sqlParseContext.LastToken = sqlToken;
        }
        #endregion 处理Token
      }
      else
      {
        //没有任何Token
        CombineSqlTask(sql, sqlParseContext);
      }
      if (queryTasksQueue.Count == 0 &&
        sqlParseContext.CurrentParallelTasks.Count == 0
        && sqlParseContext.CurrentTask != null)
      {
        var curTask = sqlParseContext.CurrentTask;
        var lastTask = sqlParseContext.LastTask;
        var sqlCurrent = curTask.Sql.ToString();
        var sqlCurrentValid = sqlCurrent.IsNotNullOrEmpty();
        var sqlLast = lastTask != null ? lastTask.Sql.ToString() : null;
        var sqlLastValid = sqlLast.IsNotNullOrEmpty();

        if (sqlCurrentValid && sqlLastValid)
        {
          // 合并Task
          CombineSqlTask(sqlCurrent, sqlParseContext);
        }
        else if (sqlLastValid && !sqlCurrentValid)
        {
          curTask = lastTask;
          sqlCurrent = sqlLast;
          sqlCurrentValid = true;
        }
        if (sqlCurrentValid)
        {
          (var resultAlias, var sqlParsed) = GetSQLAliasList(curTask.Sql.ToString());
          curTask.Sql.Clear().Append(sqlParsed);
          curTask.ResultAlias = resultAlias;
          sqlParseContext.CurrentParallelTasks.Add(curTask);
        }
      }
      queryTasksQueue.Add(sqlParseContext.CurrentParallelTasks);
      return sqlParseResult;
    }

    /// <summary>
    /// 创建一个SQLTask
    /// </summary>
    /// <param name="sqlExp"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public virtual SqlTask CreateSqlTask(string sqlExp, SqlParseContext context)
    {
      var task = new SqlTask(sqlExp)
      {
        Id = Interlocked.Increment(ref _taskCounter).ToString(),
        DataSourceId = context.LastTask?.DataSourceId ?? context.DefaultDataSourceId,
      };
      if (context.CurrentTask != null)
      {
        context.LastTask = context.CurrentTask;
      }
      context.CurrentTask = task;
      return task;
    }

    /// <summary>
    /// 两个相邻且数据源和事务策略均相同的SQLTask将被合并成一个SqlTask
    /// </summary>
    /// <param name="sqlExp"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public virtual SqlTask CombineSqlTask(string sqlExp, SqlParseContext context)
    {
      if (sqlExp.IsNullOrEmpty())
      {
        context.CurrentParallelTasks.Add(context.CurrentTask);
        return context.CurrentTask;
      }
      (var resultAlias, var sqlParsed) = GetSQLAliasList(sqlExp);
      var lastSqlTask = context.LastTask;
      var curTask = context.CurrentTask;
      var usingDataSourceId = (curTask != null ? curTask.DataSourceId : (lastSqlTask != null ? lastSqlTask.DataSourceId : context.DefaultDataSourceId));
      var needTrans = curTask != null ? curTask.Trans.Value : false;
      if (lastSqlTask != null &&
        context.LastTask.DataSourceId == usingDataSourceId &&
        needTrans == context.LastTask.Trans.Value)
      {
        //合并相同数据源且事务策略一致的SQLTask
        if (lastSqlTask.ResultAlias == null)
        {
          lastSqlTask.ResultAlias = resultAlias;
        }
        else
        {
          lastSqlTask.ResultAlias.Add(resultAlias.ToArray());
        }
        lastSqlTask.Sql.Append($"\r\n\r\n{sqlParsed}");
        context.CurrentTask = lastSqlTask;
        if (context.CurrentParallelTasks.Count == 0)
        {
          context.CurrentParallelTasks.Add(lastSqlTask);
        }
        return lastSqlTask;
      }
      else
      {
        var sqlTask = new SqlTask(sqlParsed)
        {
          Id = Interlocked.Increment(ref _taskCounter).ToString(),
          DataSourceId = usingDataSourceId,
          ResultAlias = resultAlias,
          Trans = needTrans
        };
        context.CurrentParallelTasks.Add(sqlTask);
        context.CurrentTask = sqlTask;
        return sqlTask;
      }
    }

    /// <summary>
    /// 根据SQL语句获取SQLToken对象
    /// </summary>
    /// <param name="tokenSql"></param>
    /// <returns></returns>
    public virtual SqlToken GetSqlToken(string tokenSql, SqlParseContext context, int index)
    {
      if (tokenSql.IsNullOrEmpty())
      {
        return null;
      }
      tokenSql = tokenSql.Trim();
      var semicolonIndex = tokenSql.IndexOf(';');
      if (semicolonIndex <= 0)
      {
        return null;
      }
      var spaceIndex = tokenSql.IndexOf(' ');
      if (spaceIndex > 0 && spaceIndex < semicolonIndex)
      {
        var token = tokenSql.Substring(0, spaceIndex);
        if (this[token] == null)
        {
          return null;
        }
        //确保空格在分号前面
        return new SqlToken
        {
          Token = token,
          Param = tokenSql.Substring(spaceIndex, semicolonIndex - spaceIndex).Trim(),
          Index = index,
        };
      }
      if (semicolonIndex > 0)
      {
        var token = tokenSql.Substring(0, semicolonIndex);
        if (this[token] == null)
        {
          return null;
        }
        return new SqlToken
        {
          Token = tokenSql.Substring(0, semicolonIndex),
          Index = index,
        };
      }
      return null;
    }

    /// <summary>
    /// 获取SQL语句的别名定义
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="sbSql"></param>
    /// <param name="parseRegex"></param>
    /// <returns></returns>
    protected virtual (IList<string> alias, string sql) GetSQLAliasList(string sql, Regex parseRegex = null)
    {
      var sbSql = new StringBuilder();
      var alias = new List<string>();
      if (parseRegex == null)
      {
        parseRegex = SQLAliasParseRegex;
      }
      sql = $" {sql}";
      sbSql.Append(sql);
      var matches = parseRegex.Matches(sql);
      if (matches.Count > 0)
      {
        for (int i = matches.Count - 1; i >= 0; i--)
        {
          var match = matches[i];
          var value = match.Value.Trim(' ', ':', '\r', '\n', '\t');
          sbSql.Remove(match.Index, match.Length);
          alias.Insert(0, value);
        }
      }
      return (alias, sbSql.ToString());
    }

    /// <summary>
    /// 输出SQL调试语句
    /// </summary>
    /// <param name="paramsSql"></param>
    /// <param name="parameterContext"></param>
    /// <returns></returns>
    public virtual string DebugSql(string sql, ParameterCollection parameterContext, Regex debugRegex = null)
    {
      if (debugRegex == null)
      {
        debugRegex = SQLDebugRegex;
      }
      var templateTranslator = new TemplateTranslator(debugRegex);
      var keys = ParserKeys;
      return templateTranslator.TranslateTemplate(sql, (field) =>
     {
       var key = $"@{field}";
       if (keys.Contains(key))
       {
         return key;
       }
       var val = parameterContext[field];
       if (val == null)
       {
         return key;
       }
       var dbType = val.Value.GetDBType();
       var tag = string.Empty;
       switch (dbType)
       {
         case DbType.AnsiString:
         case DbType.String:
         case DbType.Date:
         case DbType.DateTime:
           tag = "'";
           break;
       }
       return $"{tag}{val.Value}{tag}";
     });
    }
  }
}
